/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
 */

package com.huawei.rpc.common.processor;

import com.huawei.rpc.annotation.Reference;
import com.huawei.rpc.annotation.Service;
import com.huawei.rpc.client.ClientProxyFactory;
import com.huawei.rpc.server.RpcServer;
import com.huawei.rpc.server.register.ServiceObject;
import com.huawei.rpc.server.register.ServiceRegister;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @Author Lijl
 * @ClassName DefaultRpcProcessor
 * @Description rcp监听处理器 负责暴露服务、自动注入
 * @Date 2022/2/26 20:43
 * @Version 1.0
 */
@Slf4j
public class DefaultRpcProcessor  implements ApplicationListener<ContextRefreshedEvent>, DisposableBean {

    @Autowired
    private ClientProxyFactory clientProxyFactory;
    @Autowired
    private ServiceRegister serviceRegister;
    @Autowired
    private RpcServer rpcService;

    @SneakyThrows
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        if (Objects.isNull(applicationContext.getParent())){
            //开启服务
            startServer(applicationContext);
            //注入Service
            injectService(applicationContext);
        }
    }

    /**
     * @Author lijiale
     * @MethodName startServer
     * @Description 扫描服务注册注解，调用服务注册将服务注册到zookeeper中
     * @Date 16:44 2022/3/11
     * @Version 1.0
     * @param applicationContext
     * @return: void
    **/
    private void startServer(ApplicationContext applicationContext) throws Exception {
        //过滤出带有服务注册注解的实例
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(Service.class);
        if (beans.size()!=0){
            //遍历服务组装服务注册信息
            for (Object o : beans.values()) {
                List<ServiceObject> soList = new ArrayList<>();
                Class<?> clazz = o.getClass();
                Service service = clazz.getAnnotation(Service.class);
                String version = service.version();
                Class<?>[] interfaces = clazz.getInterfaces();
                if (interfaces.length>1){
                    //相同接口存在不同版本号，则分别注册
                    for (Class<?> aClass : interfaces) {
                        String aClassName = aClass.getName();
                        if (StringUtils.hasLength(version)){
                            aClassName +=":"+version;
                        }
                        soList.add(new ServiceObject(aClassName,aClass,o));
                    }
                }else{
                    Class<?> superClass = interfaces[0];
                    String aClassName = superClass.getName();
                    if (StringUtils.hasLength(version)){
                        aClassName +=":"+version;
                    }
                    soList.add(new ServiceObject(aClassName, superClass, o));
                }
                //调用服务注册
                this.serviceRegister.register(soList);
            }
            rpcService.start();
        }
    }

    /**
     * @Author lijiale
     * @MethodName injectService
     * @Description 注入远程调用服务
     * @Date 16:52 2022/3/11
     * @Version 1.0
     * @param applicationContext
     * @return: void
    **/
    private void injectService(ApplicationContext applicationContext) {
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Class<?> clazz = applicationContext.getType(beanName);
            if (Objects.isNull(clazz)){
                continue;
            }
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                Reference reference = field.getAnnotation(Reference.class);
                if (Objects.isNull(reference)){
                    continue;
                }
                Class<?> fieldClass = field.getType();
                Object bean = applicationContext.getBean(beanName);
                field.setAccessible(true);
                String version = reference.version();
                try {
                    field.set(bean,clientProxyFactory.getProxy(fieldClass,version));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * @Author lijiale
     * @MethodName destroy
     * @Description 关闭服务
     * @Date 16:50 2022/3/11
     * @Version 1.0
     * @param
     * @return: void
    **/
    @Override
    public void destroy() {
        log.info("The application will stop and the zookeeper connection will be closed");
        rpcService.stop();
    }
}
